home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / xinetd / xinetd.2.0.6 / parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-22  |  17.8 KB  |  759 lines

  1. /*
  2.  * (c) Copyright 1992 by Panagiotis Tsirigotis
  3.  * All rights reserved.  The file named COPYRIGHT specifies the terms 
  4.  * and conditions for redistribution.
  5.  */
  6.  
  7. static char RCSid[] = "$Id" ;
  8.  
  9. #include <sys/types.h>
  10. #include <netdb.h>
  11. #include <string.h>
  12. #include <syslog.h>
  13. #include <memory.h>
  14.  
  15. #include "misc.h"
  16. #include "str.h"
  17. #include "pset.h"
  18. #include "fsma.h"
  19. #include "sio.h"
  20.  
  21. #include "defs.h"
  22. #include "service.h"
  23. #include "conf.h"
  24. #include "attr.h"
  25. #include "parse.h"
  26. #include "addr.h"
  27.  
  28. char *malloc() ;
  29. void endprotoent() ;
  30. void endpwent() ;
  31. void endgrent() ;
  32. int endnetent() ;
  33. void endhostent() ;
  34.  
  35.  
  36. void parsemsg() ;
  37. void msg() ;
  38. void out_of_memory() ;
  39.  
  40. char *next_line() ;
  41. int line_has_only_1_char() ;
  42.  
  43. /*
  44.  * Parser functions
  45.  */
  46. status_e service_parser() ;
  47. status_e socket_type_parser() ;
  48. status_e protocol_parser() ;
  49. status_e wait_parser() ;
  50. status_e user_parser() ;
  51. status_e group_parser() ;
  52. status_e server_parser() ;
  53. status_e server_args_parser() ;
  54. status_e instances_parser() ;
  55. status_e log_on_success_parser() ;
  56. status_e log_on_failure_parser() ;
  57. status_e log_type_parser() ;
  58. status_e only_from_parser() ;
  59. status_e no_access_parser() ;
  60. status_e access_times_parser() ;
  61. status_e type_parser() ;
  62. status_e id_parser() ;
  63. status_e env_parser() ;
  64. status_e port_parser() ;
  65. status_e rpc_version_parser() ;
  66. status_e passenv_parser() ;
  67. status_e flags_parser() ;
  68. status_e disabled_parser() ;
  69.  
  70. /*
  71.  * A NULL value for the name field marks the end of the table
  72.  *
  73.  * The 3rd value is the number of attribute values.
  74.  * If the number is positive, exactly that many values must be specified.
  75.  * If the number is -1, 0 or more values may be specified.
  76.  * If the number is -2, 0 or more values may be specified and the operators
  77.  * '+=' and '-=' may be used.
  78.  */
  79. static struct attribute service_attributes[] =
  80.     {
  81.         { "socket_type",         A_SOCKET_TYPE,        1,        socket_type_parser         },
  82.         { "protocol",            A_PROTOCOL,            1,        protocol_parser             },
  83.         { "wait",                A_WAIT,                1,        wait_parser                    },
  84.         { "user",                 A_USER,                 1,        user_parser                    },
  85.         { "group",                 A_GROUP,                1,        group_parser                 },
  86.         { "server",                A_SERVER,            1,        server_parser                },
  87.         { "server_args",        A_SERVER_ARGS,        -1,    server_args_parser        },
  88.         { "instances",            A_INSTANCES,        1,        instances_parser            },
  89.         { "log_on_success",    A_LOG_ON_SUCCESS,    -2,    log_on_success_parser    },
  90.         { "log_on_failure",    A_LOG_ON_FAILURE,    -2,    log_on_failure_parser    },
  91.         { "log_type",            A_LOG_TYPE,            -1,    log_type_parser            },
  92.         { "only_from",            A_ONLY_FROM,        -2,    only_from_parser            },
  93.         { "no_access",            A_NO_ACCESS,        -2,    no_access_parser            },
  94.         { "access_times",        A_ACCESS_TIMES,    -1,    access_times_parser        },
  95.         { "type",                A_TYPE,                -1,    type_parser                    },
  96. #ifndef NO_RPC
  97.         { "rpc_version",        A_RPC_VERSION,        1,        rpc_version_parser        },
  98. #endif
  99.         { "id",                    A_ID,                    1,        id_parser                    },
  100.         { "env",                    A_ENV,                -2,    env_parser                    },
  101.         { "port",                A_PORT,                1,        port_parser                    },
  102.         { "passenv",            A_PASSENV,            -2,    passenv_parser                },
  103.         { "flags",                A_FLAGS,                -1,    flags_parser                },
  104.         { NULL,                    A_NONE,                -1,    NULL                            }
  105.     } ;
  106.  
  107. static struct attribute default_attributes[] =
  108.    {
  109.       { "log_type",        A_LOG_TYPE,       -2,   log_type_parser       },
  110.       { "log_on_success",  A_LOG_ON_SUCCESS, -2,   log_on_success_parser },
  111.       { "log_on_failure",  A_LOG_ON_FAILURE, -2,   log_on_failure_parser },
  112.       { "disabled",        A_DISABLED,       -2,   disabled_parser       },
  113.       { "no_access",       A_NO_ACCESS,      -2,   no_access_parser      },
  114.       { "only_from",       A_ONLY_FROM,      -2,   only_from_parser      },
  115.       { "instances",       A_INSTANCES,      1,    instances_parser      },
  116.       { "passenv",         A_PASSENV,        -2,   passenv_parser        },
  117.       { NULL,              A_NONE,           0,    NULL                      }
  118.    } ;
  119.  
  120.  
  121. #define MODIFIABLE( ap )                ( (ap)->n_values == -2 )
  122. #define VAR_VALUES( ap )                ( (ap)->n_values < 0 )
  123. #define FIXED_VALUES( ap )                ( (ap)->n_values > 0 )
  124.  
  125. int line_count ;
  126.  
  127.  
  128.  
  129. /*
  130.  * Given the id, return the name (only the service attributes are searched)
  131.  */
  132. char *attr_name_lookup( id )
  133.     register int id ;
  134. {
  135.     register struct attribute *ap ;
  136.  
  137.     for ( ap = &service_attributes[ 0 ] ; ap->name != NULL ; ap++ )
  138.         if ( id == ap->id )
  139.             return( ap->name ) ;
  140.     return( NULL ) ;
  141. }
  142.  
  143.  
  144. /*
  145.  * Parsing rules and rationale
  146.  *
  147.  * The parse_conf_file function parses a configuration file identified
  148.  * by a file descriptor and fills the service table and defaults of
  149.  * the configuration argument.
  150.  *
  151.  * The configuration information for a service comes from 2 sources: the
  152.  * service entry and, possibly, the defaults entry.
  153.  * Attributes specified in the defaults entry can be overriden or
  154.  * modified by the service entry. Modifiable attributes can be identified
  155.  * by the value -2 for the 'n_values' field of the struct attribute. Those
  156.  * attributes with a different value for 'n_values' are overridable ones.
  157.  * The modifiable attributes are filled in only if the entry tries to modify
  158.  * them.
  159.  */
  160.  
  161. /*
  162.  * Read the configuration file (descriptor fd) and place all
  163.  * services found there in the configuration.
  164.  */
  165. void parse_conf_file( fd, confp )
  166.     int fd ;
  167.     struct configuration *confp ;
  168. {
  169.     pset_h stab = confp->services ;
  170.     struct service_config *default_config = CONF( confp->defaults ) ;
  171.     struct service_config default_default_config ;
  172.     boolean_e found_defaults = NO ;
  173.     void get_service_entry() ;
  174.     void skip_entry() ;
  175.     entry_e find_next_entry() ;
  176.     status_e parse_entry() ;
  177.     char *func = "parse_conf_file" ;
  178.  
  179.     line_count = 0 ;
  180.     CLEAR( default_default_config ) ;
  181.  
  182.     for ( ;; )
  183.     {
  184.         entry_e entry_type ;
  185.         char *service_name ;
  186.  
  187.         /*
  188.          * if find_next_entry is successful, service_name
  189.          * will point to malloc'ed memory
  190.          */
  191.         entry_type = find_next_entry( fd, &service_name ) ;
  192.         if ( entry_type == SERVICE_ENTRY )
  193.             get_service_entry( fd, stab, service_name, default_config ) ;
  194.         else if ( entry_type == DEFAULTS_ENTRY )
  195.         {
  196.             if ( found_defaults == YES )
  197.             {
  198.                 parsemsg( LOG_ERR, func,
  199.                  "only 1 defaults entry is allowed. This entry will be ignored" ) ;
  200.                 skip_entry( fd ) ;
  201.             }
  202.             else if ( parse_entry( DEFAULTS_ENTRY, fd,
  203.                                     default_config, &default_default_config ) == OK )
  204.                 found_defaults = YES ;
  205.         }
  206.         else
  207.             break ;
  208.     }
  209.  
  210.     endprotoent() ;
  211.     endpwent() ;
  212.     endgrent() ;
  213.     endnetent() ;
  214.     endhostent() ;
  215. }
  216.  
  217.  
  218. /*
  219.  * Find the next service entry.
  220.  * Look for a line of the form:
  221.  *
  222.  *        <white-space> service <white-space> <service_name>
  223.  *
  224.  * followed by a line containing only the ENTRY_BEGIN character
  225.  */
  226. PRIVATE entry_e find_next_entry( fd, snamep )
  227.     int fd ;
  228.     char **snamep ;                        /* service name pointer */
  229. {
  230.     register char *line = next_line( fd ) ;
  231.     register char *p ;
  232.     str_h strp ;
  233.     char *sname ;
  234.     entry_e entry_type ;
  235.     char *func = "find_next_entry" ;
  236.  
  237.     if ( line == NULL )
  238.         return( NO_ENTRY ) ;
  239.     
  240.     strp = str_parse( line, " \t", STR_RETURN_ERROR, (int *)0 ) ;
  241.     if ( strp == NULL )
  242.     {
  243.         parsemsg( LOG_CRIT, func, "str_parse failed" ) ;
  244.         return( BAD_ENTRY ) ;
  245.     }
  246.     if ( ( p = str_component( strp ) ) == NULL )
  247.     {
  248.         /*
  249.          * This shouldn't happen since it implies that there is a bug
  250.          * in next_line
  251.          */
  252.         parsemsg( LOG_WARNING, func, "empty line" ) ;
  253.         str_endparse( strp ) ;
  254.         return( BAD_ENTRY ) ;
  255.     }
  256.  
  257.     /*
  258.      * Look for a keyword:
  259.      *        service:     for a new service entry
  260.      *        defaults:     for the defaults entry
  261.      */
  262.     if ( EQ( p, KW_SERVICE ) )
  263.     {
  264.         /*
  265.          * Now get the service name
  266.          */
  267.         if ( ( p = str_component( strp ) ) == NULL )
  268.         {
  269.             parsemsg( LOG_ERR, func, "service name missing" ) ;
  270.             str_endparse( strp ) ;
  271.             return( BAD_ENTRY ) ;
  272.         }
  273.     
  274.         sname = make_string( 1, p ) ;
  275.         if ( sname == NULL )
  276.         {
  277.             out_of_memory( func ) ;
  278.             str_endparse( strp ) ;
  279.             return( BAD_ENTRY ) ;
  280.         }
  281.         str_endparse( strp ) ;
  282.         entry_type = SERVICE_ENTRY ;
  283.     }
  284.     else if ( EQ( p, KW_DEFAULTS ) )
  285.     {
  286.         str_endparse( strp ) ;
  287.         entry_type = DEFAULTS_ENTRY ;
  288.     }
  289.     else
  290.     {
  291.         parsemsg( LOG_ERR, func, "missing service keyword" ) ;
  292.         str_endparse( strp ) ;
  293.         return( BAD_ENTRY ) ;
  294.     }
  295.  
  296.     /*
  297.      * Now look for ENTRY_BEGIN
  298.      */
  299.     line = next_line( fd ) ;
  300.     if ( line == NULL || ! line_has_only_1_char( line, ENTRY_BEGIN ) )
  301.     {
  302.         parsemsg( LOG_ERR, func,
  303.             "Service %s: couldn't find %c", sname, ENTRY_BEGIN ) ;
  304.         if ( entry_type == SERVICE_ENTRY )
  305.             free( sname ) ;
  306.         return( BAD_ENTRY ) ;
  307.     }
  308.     *snamep = sname ;
  309.     return( entry_type ) ;
  310. }
  311.  
  312.  
  313.  
  314. /*
  315.  * Get a service entry. Steps:
  316.  *
  317.  *        1. Parse entry attributes
  318.  *        2. Determine service id
  319.  *        3. Insert entry in service table
  320.  */
  321. PRIVATE void get_service_entry( fd, stab, name, defaults )
  322.     int fd ;
  323.     pset_h stab ;
  324.     char *name ;
  325.     struct service_config *defaults ;
  326. {
  327.     struct service_config sconf ;
  328.     register struct service_config *scp = &sconf ;
  329.     unsigned u ;
  330.     char *func = "get_service_entry" ;
  331.     status_e parse_entry() ;
  332.     
  333.     CLEAR( sconf ) ;
  334.     scp->name = name ;
  335.  
  336.     if ( parse_entry( SERVICE_ENTRY, fd, scp, defaults ) == FAILED )
  337.     {
  338.         sconf_free( scp ) ;
  339.         skip_entry( fd ) ;
  340.         return ;
  341.     }
  342.  
  343.    /*
  344.     * If no service id was specified, set it equal to the service name
  345.     */
  346.    if ( ! SPECIFIED( scp, A_ID ) )
  347.       if ( scp->id = make_string( 1, scp->name ) )
  348.          PRESENT( scp, A_ID ) ;
  349.       else
  350.       {
  351.             out_of_memory( func ) ;
  352.          sconf_free( scp ) ;
  353.          return ;
  354.       }
  355.  
  356.     /*
  357.      * Look it up
  358.      */
  359.     for ( u = 0 ; u < pset_count( stab ) ; u++ )
  360.         if ( EQ( CONF( SP( pset_pointer( stab, u ) ) )->id, scp->id ) )
  361.         {
  362.          parsemsg( LOG_ERR, func, "id not unique: %s", scp->id ) ;
  363.             sconf_free( scp ) ;
  364.             return ;
  365.         }
  366.  
  367.     if ( svc_alloc( scp, stab ) == NULL )
  368.     {
  369.         sconf_free( scp ) ;
  370.         return ;
  371.     }
  372. }
  373.  
  374.  
  375. #define INIT_PSET( pset )                                                  \
  376.          if ( pset == NULL && ( pset = pset_create( 10, 10 ) ) == NULL )   \
  377.          {                                                                 \
  378.                 out_of_memory( func ) ;                                                        \
  379.             return( FAILED ) ;                                             \
  380.          }
  381.  
  382. /*
  383.  * Read the entry line-by-line and add the information in scp
  384.  * Use defaults to initialize modifiable entry fields.
  385.  */
  386. PRIVATE status_e parse_entry( entry_type, fd, scp, defaults )
  387.     entry_e entry_type ;
  388.     int fd ;
  389.     struct service_config *scp ;
  390.     struct service_config *defaults ;
  391. {
  392.    static pset_h attr_values ;
  393.    register char *line ;
  394.    char *attr_name ;
  395.    enum assign_op op ;
  396.    void identify_attribute() ;
  397.    status_e parse_line() ;
  398.    char *func = "get_attributes" ;
  399.  
  400.    INIT_PSET( attr_values ) ;
  401.  
  402.    for ( ;; )
  403.    {
  404.       line = next_line( fd ) ;
  405.       if ( line == NULL )
  406.       {
  407.          parsemsg( LOG_ERR, func, "incomplete entry" ) ;
  408.          return( FAILED ) ;
  409.       }
  410.  
  411.       if ( line_has_only_1_char( line, ENTRY_END ) )
  412.          return( OK ) ;
  413.  
  414.       if ( parse_line( line, &attr_name, &op, attr_values ) == FAILED )
  415.       {
  416.          pset_clear( attr_values ) ;
  417.          return( FAILED ) ;
  418.       }
  419.  
  420.         identify_attribute( entry_type,
  421.                     scp, defaults, attr_name, op, attr_values ) ;
  422.       pset_clear( attr_values ) ;
  423.    }
  424. }
  425.  
  426.  
  427.  
  428. /*
  429.  * Find the attribute with the specified name
  430.  */
  431. PRIVATE struct attribute *attr_lookup( attr_array, attr_name )
  432.     struct attribute attr_array[] ;
  433.     char *attr_name ;
  434. {
  435.     register struct attribute *ap ;
  436.     char *func = "attr_lookup" ;
  437.  
  438.     for ( ap = &attr_array[ 0 ] ; ap->name != NULL ; ap++ )
  439.         if ( EQ( attr_name, ap->name ) )
  440.             return( ap ) ;
  441.     parsemsg( LOG_WARNING, func, "bad attribute: %s", attr_name ) ;
  442.     return( NULL ) ;
  443. }
  444.  
  445.  
  446. /*
  447.  * Identify the attribute in <attr_name>.
  448.  *
  449.  * Check if
  450.  *        1) the attribute has been defined already
  451.  *        2) the value count is correct
  452.  *        3) the assign op is appropriate
  453.  *
  454.  * Invoke appropriate parser
  455.  */
  456. PRIVATE void identify_attribute( entry_type, scp, defaults,
  457.                                                         attr_name, op, attr_values )
  458.     entry_e entry_type ;
  459.     struct service_config *scp ;
  460.     struct service_config *defaults ;
  461.     register char *attr_name ;
  462.     enum assign_op op ;
  463.     pset_h attr_values ;
  464. {
  465.     register struct attribute *ap ;
  466.     char *func = "identify_attribute" ;
  467.     void fill_attribute() ;
  468.  
  469.     if ( entry_type == SERVICE_ENTRY )
  470.         ap = attr_lookup( service_attributes, attr_name ) ;
  471.     else
  472.         ap = attr_lookup( default_attributes, attr_name ) ;
  473.     
  474.     if ( ap == NULL )
  475.         return ;
  476.  
  477.     if ( ! MODIFIABLE( ap ) )
  478.     {
  479.         if ( SPECIFIED( scp, ap->id ) )
  480.         {
  481.             parsemsg( LOG_WARNING, func,
  482.                 "Service %s: attribute already set: %s",
  483.                         scp->name, attr_name ) ;
  484.             return ;
  485.         }
  486.  
  487.         if ( op != SET_EQ )
  488.         {
  489.             parsemsg( LOG_WARNING, func,
  490.                 "Service %s: operator %s cannot be used for attribute %s",
  491.                     scp->name, ( op == PLUS_EQ ) ? "+=" : "-=", attr_name ) ;
  492.             return ;
  493.         }
  494.     }
  495.     else
  496.     {
  497.         if ( entry_type == DEFAULTS_ENTRY && op == SET_EQ )
  498.             op = PLUS_EQ ;
  499.         if ( ! IS_PRESENT( scp, ap->id ) && SPECIFIED( defaults, ap->id ) )
  500.             fill_attribute( ap->id, scp, defaults ) ;
  501.     }
  502.  
  503.     if ( FIXED_VALUES( ap ) && ap->n_values != pset_count( attr_values ) )
  504.     {
  505.         parsemsg( LOG_WARNING, func,
  506.             "attribute %s expects %d values and %d values were specified",
  507.             attr_name, ap->n_values, pset_count( attr_values ) ) ;
  508.         return ;
  509.     }
  510.  
  511.     if ( (*ap->parser)( attr_values, scp, op ) == OK )
  512.         SPECIFY( scp, ap->id ) ;
  513. }
  514.  
  515.  
  516.  
  517. /*
  518.  * Fill in scp the value of the modifiable attribute attr from def.
  519.  * These modifiable attributes are:
  520.  *        log_on_{success,failure}
  521.  *        only_from
  522.  *        no_access
  523.  *        passenv
  524.  */
  525. PRIVATE void fill_attribute( attr_id, scp, def )
  526.     unsigned attr_id ;
  527.     struct service_config *scp ;
  528.     struct service_config *def ;
  529. {
  530.     status_e list_assign() ;
  531.     status_e copy_pset() ;
  532.  
  533.     switch ( attr_id )
  534.     {
  535.         case A_LOG_ON_SUCCESS:
  536.             M_ASSIGN( scp->log_on_success, def->log_on_success ) ;
  537.             PRESENT( scp, A_LOG_ON_SUCCESS ) ;
  538.             break ;
  539.  
  540.         case A_LOG_ON_FAILURE:
  541.             M_ASSIGN( scp->log_on_failure, def->log_on_failure ) ;
  542.             PRESENT( scp, A_LOG_ON_FAILURE ) ;
  543.             break ;
  544.  
  545.         case A_ONLY_FROM:
  546.             if ( addrlist_copy( def->only_from, &scp->only_from ) == OK )
  547.                 PRESENT( scp, A_ONLY_FROM ) ;
  548.             break ;
  549.  
  550.         case A_NO_ACCESS:
  551.             if ( addrlist_copy( def->no_access, &scp->no_access ) == OK )
  552.                 PRESENT( scp, A_NO_ACCESS ) ;
  553.             break ;
  554.         
  555.         case A_PASSENV:
  556.             if ( copy_pset( def->passenv, &scp->passenv, (fsma_h) NULL ) == OK )
  557.                 PRESENT( scp, A_PASSENV ) ;
  558.             break ;
  559.     }
  560. }
  561.  
  562.  
  563.  
  564.  
  565. /*
  566.  * Parse a line of the form:
  567.  *            name OP value value value ...
  568.  * where each value is a string and OP can be '=', '+=', '-='
  569.  *
  570.  * NOTE: We do not allocate space for the name and values. Instead we keep
  571.  *            pointers to the line.
  572.  */
  573. PRIVATE status_e parse_line( line, namep, opp, values )
  574.     char *line ;
  575.     char **namep ;
  576.     enum assign_op *opp ;
  577.     pset_h values ;
  578. {
  579.     char *value ;
  580.     char *values_string ;
  581.     char *attribute ;
  582.     str_h strp ;
  583.     char *func = "parse_line" ;
  584.     char *get_attr_op() ;
  585.  
  586.     if ( ( values_string = get_attr_op( line, &attribute, opp ) ) == NULL )
  587.         return( FAILED ) ;
  588.  
  589.     /*
  590.      * Now grab the values
  591.      */
  592.     strp = str_parse( values_string, " \t", STR_RETURN_ERROR, (int *)0 ) ;
  593.     if ( strp == NULL )
  594.     {
  595.         parsemsg( LOG_CRIT, func, "out of memory" ) ;
  596.         return( FAILED ) ;
  597.     }
  598.  
  599.     while ( value = str_component( strp ) )
  600.     {
  601.         if ( pset_add( values, value ) == NULL )
  602.         {
  603.             parsemsg( LOG_CRIT, func,
  604.                     "Out of memory (attribute = %s)", attribute ) ;
  605.             str_endparse( strp ) ;
  606.             return( FAILED ) ;
  607.         }
  608.     }
  609.  
  610.     str_endparse( strp ) ;
  611.     *namep = attribute ;
  612.     return( OK ) ;
  613. }
  614.  
  615.  
  616. /*
  617.  * Input:
  618.  *        a line of the form
  619.  *                name [SPACE] OP [SPACE] value [SPACE] value ...
  620.  *
  621.  * Recognize the attribute name and operator and place them in *attrp, *opp
  622.  *
  623.  * Currently, we allow any non-space character to be used in the
  624.  * attribute name.
  625.  *
  626.  * Return value: a pointer to the character after OP.
  627.  */
  628. PRIVATE char *get_attr_op( line, attrp, opp )
  629.     char *line ;
  630.     char **attrp ;
  631.     enum assign_op *opp ;
  632. {
  633.     register char *p ;
  634.     char *attr ;
  635.     enum assign_op op ;
  636.     char *func = "get_attr_op" ;
  637.  
  638.     /*
  639.      * First get the attribute name
  640.      */
  641.     for ( p = line ; isspace( *p ) ; p++ ) ;        /* skip spaces */
  642.     if ( *p == NUL )
  643.     {
  644.         parsemsg( LOG_ERR, func, "Empty line" ) ;
  645.         return( NULL ) ;
  646.     }
  647.  
  648.     attr = p ;
  649.     for ( ; ! isspace( *p ) ; p++ ) ;                /* skip attribute name */
  650.     if ( *p == NUL )
  651.     {
  652.         parsemsg( LOG_ERR, func, "Nothing after attribute: %s", attr ) ;
  653.         return( NULL ) ;
  654.     }
  655.     *p++ = NUL ;            /* now attribute name is NUL terminated */
  656.  
  657.  
  658.     for ( ; isspace( *p ) ; p++ ) ;        /* skip spaces */
  659.  
  660.     switch ( *p )
  661.     {
  662.         case NUL:
  663.             parsemsg( LOG_ERR, func, "Nothing after attribute: %s", attr ) ;
  664.             return( NULL ) ;
  665.     
  666.         case '=':
  667.             op = SET_EQ ;
  668.             break ;
  669.         
  670.         case '+':
  671.         case '-':
  672.             op = ( *p++ == '+' ) ? PLUS_EQ : MINUS_EQ ;
  673.             if ( *p == '=' )
  674.                 break ;
  675.             
  676.             /* FALL THROUGH if there is no '=' after the '+' or '-' */
  677.         
  678.         default:
  679.             parsemsg( LOG_ERR, func, "Bad operator for attribute: %s", attr ) ;
  680.             return( NULL ) ;
  681.     }
  682.     *attrp = attr ;
  683.     *opp = op ;
  684.     return( ++p ) ;        /* skip the '=' */
  685. }
  686.  
  687.  
  688. PRIVATE void skip_entry( fd )
  689.     register int fd ;
  690. {
  691.     for ( ;; )
  692.     {
  693.         register char *line = next_line( fd ) ;
  694.  
  695.         if ( line == NULL )            /* reached EOF ? */
  696.         {
  697.             parsemsg( LOG_WARNING, "skip_entry",
  698.                 "missing %c in last service entry", ENTRY_END ) ;
  699.             break ;
  700.         }
  701.  
  702.         if ( line_has_only_1_char( line, ENTRY_END ) )
  703.             break ;
  704.     }
  705. }
  706.  
  707.  
  708.  
  709. /*
  710.  * next_line returns the next line of the file or NULL if the end of file
  711.  * is reached.
  712.  * Comment lines and empty lines are skipped.
  713.  */
  714. PRIVATE char *next_line( fd )
  715.    int fd ;
  716. {
  717.    for ( ;; )
  718.    {
  719.       register char *p ;
  720.       register char *line = Srdline( fd ) ;
  721.  
  722.       if ( line == NULL )
  723.          return( NULL ) ;
  724.  
  725.       line_count++ ;
  726.  
  727.       for ( p = line ;; p++ )
  728.          if ( *p == NUL || *p == COMMENT_BEGIN )
  729.             break ;                                /* skip this line */
  730.          else if ( isspace( *p ) )
  731.             continue ;                             /* skip white space */
  732.          else
  733.             return( line ) ;
  734.    }
  735. }
  736.  
  737.  
  738.  
  739. /*
  740.  * Returns TRUE if the given line contains a single instance of the
  741.  * specified character and no other non-space characters
  742.  */
  743. PRIVATE int line_has_only_1_char( line, ch )
  744.    char *line ;
  745.    char ch ;
  746. {
  747.    register char *p ;
  748.    register char target_char = ch ;
  749.  
  750.    for ( p = line ; *p ; p++ )
  751.       if ( *p == target_char )
  752.          target_char = NUL ;
  753.       else if ( ! isspace( *p ) )
  754.          return( FALSE ) ;
  755.    return( target_char != ch ) ;
  756. }
  757.  
  758.  
  759.